-
Notifications
You must be signed in to change notification settings - Fork 839
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use react-virtualized to virtualize EuiComboBox options list #670
Conversation
@cjcenizal I think I have gone about as far as I can with this. Remaining questions/actions
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking so awesome! I created nreese#1 to address the text truncation and do some minor cleanup. It should address some of the comments I left below.
I think virtualization should be always-on by default. This will keep the interface simpler so the consumer doesn't need to worry about this. And if it turns out we should allow the consumer to control this then it's easier to add a prop than to remove one.
Does a prop need to be added for rendering a group title?
I don't have the cycles to think this through, so for now I'm comfortable punting on this. My gut tells me we should see what kinds of use cases people have for rendering options and virtualization and then make a decision once we have more info. Will you be using custom rendering in your use case?
We also need to add another checkbox to the list of to-do's:
- Fix keyboard navigation, i.e. using the up and down arrows to go through the list of options
It works surprisingly well when just using the down arrow key, but it doesn't wrap to the end when you use the up arrow key to go past the first option.
Also, somehow I got this error by using the arrow keys to navigating and then clicking on the input:
@@ -485,6 +490,7 @@ export class EuiComboBox extends Component { | |||
let optionsList; | |||
|
|||
if (!noSuggestions && isListOpen) { | |||
console.log("width", width); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leftover console.log.
overflow: hidden; | ||
} | ||
|
||
.ReactVirtualized__List { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we put this inside of the .euiComboBoxOptionsList
class so these styles are scoped to this component?
.euiComboBoxOptionsList {
@include euiFormControlSize;
box-sizing: content-box; /* 1 */
margin-left: -1px; /* 1 */
z-index: $euiZComboBox;
position: absolute; /* 2 */
top: 0; /* 2 */
.ReactVirtualized__List {
@include euiScrollBar;
}
}
.euiComboBoxTitle { | ||
font-size: $euiFontSizeXS; | ||
padding: $euiSizeXS $euiSizeS $euiSizeXS 0; | ||
padding: ($euiSizeXS + $euiSizeS - 1px) $euiSizeS $euiSizeXS 0; /* 1 */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we tweak this to be:
padding: ($euiSizeXS + $euiSizeS - 1px) $euiSizeS $euiSizeXS;
And then can we edit .euiComboBoxOption
so that its padding (line 3 of _combo_box_option.scss) is this:
padding: $euiSizeXS $euiSizeS $euiSizeXS #{$euiSizeM + $euiSizeXS};
optionsList.push(renderedOption); | ||
}); | ||
const optionHeight = 27; // TODO dynamically figure this out | ||
const numVisiableOptions = matchingOptions.length < 7 ? matchingOptions.length : 7; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor typo, this should be numVisibleOptions
}); | ||
if (matchingOptionsForGroup.length > 0) { | ||
// Add option for group label | ||
matchingOptions.push({ label: option.label, isGroupLabelOption: true }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice improvement!
No, my use case will not use custom rendering. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Focus tracking looks great. I found a couple more things and submitted nreese#2 to address them. Once we merge it I think we're good to go.
}; | ||
|
||
clearActiveOption = () => { | ||
this.activeOptionIndex = undefined; | ||
this.state.activeOptionIndex = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we want to call setState
here, right?
88c1476
to
1a45da9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🌊 Rad!! I only had a few comments, all minor.
code: virtualizedHtml, | ||
}], | ||
text: ( | ||
<p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we tweak this to fix some spacing and a typo:
<p>
<EuiCode>EuiComboBoxList</EuiCode> uses <Link to="https://github.com/bvaughn/react-virtualized">react-virtualized</Link>{' '}
to only render visible options to be super fast no matter how many options there are.
</p>
<EuiCode>option</EuiCode> object to store metadata about the option for use in this callback. | ||
</p> | ||
<Fragment> | ||
<p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will fix some more spacing issues:
<p>
You can provide a <EuiCode>renderOption</EuiCode> prop which will accept <EuiCode>option</EuiCode>{' '}
and <EuiCode>searchValue</EuiCode> arguments. Use the <EuiCode>value</EuiCode> prop of the{' '}
<EuiCode>option</EuiCode> object to store metadata about the option for use in this callback.
</p>
@@ -149,7 +151,7 @@ export class EuiComboBox extends Component { | |||
tabbableItems[comboBoxIndex + amount].focus(); | |||
}; | |||
|
|||
incrementActiveOptionIndex = amount => { | |||
incrementActiveOptionIndex = _.throttle(amount => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we import the throttle
module?
import { throttle } from 'lodash';
this.setState({ | ||
activeOptionIndex: nextActiveOptionIndex, | ||
}); | ||
}, 200); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
200 feels a bit sluggish to me -- 100 feels snappier, can we use that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with 100 I was still getting the keydown to be faster than the rendering and I could hold the key down and the focus option would not keep up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@snide I made the options focusable so that screen readers can pick them up and read them out as "disabled". Otherwise someone using a screen reader won't know that the option is in the list. This can be a little weird for sighted users but I think it's worth it for the accessibility gain. What do you think? You're right about the custom rendering example; it would have to be handled by the consumer. I thought about updating the example to truncate the text, but it would mean introducing a custom class for a single example and I wasn't sure it was worth it. We can update the example text to provide guidance on this though. |
I think it shouldn't be focusable, for similar reasons that @cchaos had the last time this came up in #653 (comment). I think its safest to follow what the browser provides for disabled states on button tags. I don't feel strongly enough on it to prevent this PR from being merged though since it's a great improvement. |
…d title attribute to options for usability. (#1)
* Call setState instead of setting activeOptionIndex directly. * Clear activeOptionIndex when you click the input. * Prevent a lot of input from overflowing the container. * Allow disabled options to be focused but not selected.
319c7e4
to
c355fed
Compare
I think it's OK to keep it in this case because it replicates the behavior of a The only aspect which is odd is that you can focus the disabled item. Sighted users will have the "not-allowed" cursor and dimmed visual state to signal that the item can't be selected, so I think it's a worthwhile tradeoff. Let's keep it in for now and get @aphelionz's input on this at some point. Maybe he knows of a better solution. |
This reverts commit 47fa3ef.
fixes #651
paired with @cjcenizal to convert EuiComboBox rendering to use react-virtualized.
Rendering long lists was really slow because each list item was added to the DOM. With virtualization, only the list items that are visible in the EuiComboBoxOptionsList panel are in the DOM and there is great performance for large lists.